package com.zbb.exam_system.service.Impl;

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.exception.excel.ExcelImportException;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zbb.exam_system.bean.JwtOperation;
import com.zbb.exam_system.constant.Method;
import com.zbb.exam_system.dto.JsonResult;
import com.zbb.exam_system.dto.TableDto;
import com.zbb.exam_system.dto.UserDto;
import com.zbb.exam_system.entity.*;
import com.zbb.exam_system.exception.ExamException;
import com.zbb.exam_system.exception.ParamException;
import com.zbb.exam_system.mapper.RelationMapper;
import com.zbb.exam_system.mapper.UserMapper;
import com.zbb.exam_system.mapper.UserRoleMapper;
import com.zbb.exam_system.service.UserService;
import com.zbb.exam_system.utils.ExamUtils;
import com.zbb.exam_system.utils.HttpUtils;
import com.zbb.exam_system.vo.StudentVo;
import com.zbb.exam_system.vo.UpdateUserPwdVo;
import com.zbb.exam_system.vo.UserVo;
import com.zbb.template.MyRedisTemplate;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import static com.zbb.exam_system.constant.BusinessConstant.*;

/**
 * @author zbbstart
 * @date 2021/2/8 - 15:35
 */
@Service
@Slf4j
public class UserServiceImpl implements UserService {
    @Autowired
    private MyRedisTemplate myRedisTemplate;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RelationMapper relationMapper;
    @Autowired
    private JwtOperation jwtOperation;
    @Autowired
    private PasswordEncoder encoder;
    @Autowired
    private HttpUtils httpUtils;
    @Autowired
    private ExamUtils examUtils;
    @Autowired
    private UserRoleMapper userRoleMapper;

    @Override
    public List<Integer> getUserRolesByUserId(Integer userId) {
        if (null == userId) {
            throw new ExamException("不能为空");
        }
        return userMapper.getUserRolesByUserId(userId);
    }

    @Override
    public User getUserInfoByUserName(String username) {
        if (StringUtils.isBlank(username)) {
            throw new ExamException("用户名称不能为空");
        }
        return userMapper.selectOne((Wrapper<User>) new QueryWrapper().eq("username", username));
    }

    @Override
    public void login(UserVo userVo) {
        if (StringUtils.isBlank(userVo.getUsername())) {
            throw new ExamException("用户名称不能为空");
        }
        if (StringUtils.isBlank(userVo.getPassword())) {
            throw new ExamException("密码不能为空");
        }
        if (null == userVo.getUserRoleId()) {
            throw new ExamException("请选择您的身份");
        }
        User user = getUserInfoByUserName(userVo.getUsername());
        if (null == user) {
            throw new ExamException("当前用户不存在...");
        }
        //获取用户的编号
        Integer userId = user.getId();
        //判断用户的状态
        if (user.getStatus() == 0){
            throw new ExamException(ERROR_INFO);
        }
        if (!(encoder.matches(userVo.getPassword(), user.getPassword()))) {
            //跟用户相关的键
            String key = LOGIN_COUNT + userId;
            //登录超过3次的冻结处理
            if (myRedisTemplate.exists(key) && myRedisTemplate.get(key).equals(LOGIN_MAX_COUNT)) {
                userMapper.update(new User(),new UpdateWrapper<User>()
                .eq("id",userId)
                .set("status",0));
            }
            //登录超三次冻结
            if (myRedisTemplate.exists(key)) {
                String count = myRedisTemplate.get(key);
                if (count.equals(LOGIN_MAX_COUNT)) {
                    throw new ExamException(ERROR_INFO);
                } else {
                    myRedisTemplate.incr(key);
                }
            } else {
                myRedisTemplate.incr(key);
                myRedisTemplate.expire(key, 30 * 60);
            }
            throw new ExamException("密码不正确");
        }
        List<Integer> roleIds = userMapper.getUserRolesByUserId(user.getId());
        if (!roleIds.contains(userVo.getUserRoleId())) {
            throw new ExamException("非法身份");
        }
    }

    @Override
    public UserDto getUserInfoByToken(String token) {
        //登录成功前删除存在Redis数据库中的登录次数的key
        //myRedisTemplate.del(LOGIN_COUNT);
        //头信息中没有token
        if (org.apache.commons.lang3.StringUtils.isBlank(token)) {
            throw new ParamException("请先进行登录");
        }
        //获取Claims
        Claims claims = jwtOperation.parseJwt(token);
        //根据token无法得到claims
        if (null == claims) {
            throw new ParamException("请先进行登录");
        }
        //获取用户信息
        return JSON.parseObject(String.valueOf(claims.get("user")), UserDto.class);
    }

    @Override
    public JsonResult updateUserPwd(UpdateUserPwdVo userPwdVo) {
        User user = userMapper.selectOne((Wrapper<User>) new QueryWrapper().eq("id", userPwdVo.getUserId()));
        //当比较密码是否一致的时候，用encoder.matches()，前面是明文密码，后面是数据库的加密密码
        if (!encoder.matches(userPwdVo.getOldPwd(), user.getPassword())) {
            throw new ExamException("您输入的旧密码不正确，请您重新输入!");
        }
        if (!userPwdVo.getNewPwd().equals(userPwdVo.getNewPwdConfirm())) {
            throw new ExamException("两次密码不一致，请重新设置");
        }
        //修改密码
        userMapper.update(new User(), new UpdateWrapper<User>()
                .eq("id", userPwdVo.getUserId())
                .set("password", encoder.encode(userPwdVo.getNewPwdConfirm())));
        return httpUtils.buildSuccess(Method.POST, "修改成功!");
    }

    @Override
    public TableDto getAllTeacherUsersByPage(Integer pageNo, Integer size) {
        //分页查询 pageNo 页码，size 页面的条数
        Page<User> page = new Page<>(pageNo, size);
        //分页的查询条件
        QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("descn", TEACHER);
        //查询
        Page<User> userPage = userMapper.selectPage(page, wrapper);
        //获取分页的数据
        List<User> userList = userPage.getRecords();
        if (CollectionUtils.isEmpty(userList)) {
            return examUtils.setTableDto();
        }
        //将密码置空
        userList.forEach(u -> u.setPassword(""));
        //获取总条数
        long total = userPage.getTotal();
        List<Teacher> teachers = new ArrayList<>();
        for (User user : userList) {
            teachers.add(new Teacher(examUtils.zeroFillFour(user.getId()), user.getUsername()));
        }
        return examUtils.setTableDto(total, teachers);
    }

    @Override
    public void insertTeacherUser(User user) {
        //判断获取的用户名是否为空
        if (null == user.getUsername()) {
            throw new ExamException("用户名称不能为空");
        }
        //查询数据库中是否有同名的老师
        User userData = userMapper.selectOne(new QueryWrapper<User>().eq("username", user.getUsername()));
        if (null != userData) {
            //模糊查询数据库中叫该中文名字的老师有多少个
            Integer usernameCount = userMapper.selectCount(new QueryWrapper<User>().eq("descn", TEACHER).like("username", user.getUsername()));
            //实际比查出来的数量多一
            Integer num = usernameCount + 1;
            //利用字符串的拼接技术重新设置老师用户的名字
            user.setUsername(user.getUsername() + num);
        }
        //默认密码是加密的 123456
        user.setPassword(DEFAULT_PASSWORD);
        //用户默认状态码是1可用状态
        user.setStatus(DEFAULT_STATUS);
        //设置用户的角色信息
        user.setDescn(TEACHER);
        userMapper.insert(user);
        //获取插入用户的自增主键值
        Integer userId = user.getId();
        UserRole userRole = new UserRole();
        userRole.setUserId(userId);
        //老师拥有老师的权限
        userRole.setRoleId(TEACHER_PERMIT_VALUE);
        userRoleMapper.insert(userRole);
        //，和学生的权限
//        userRole.setRoleId(STUDENT_PERMIT_VALUE);
//        userRoleMapper.insert(userRole);
    }

    @Override
    public void deleteTeacherUsers(List<Integer> teacherNos) {
        userMapper.deleteBatchIds(teacherNos);
        userRoleMapper.delete(new QueryWrapper<UserRole>().in("user_id", teacherNos));
    }

    @Override
    public void updateTeacherName(User user) {
        userMapper.update(user, new UpdateWrapper<User>()
                .eq("id", user.getId())
                .set("username", user.getUsername()));
    }

    @Override
    public TableDto getAllStudentUsersByPage(Integer pageNo, Integer size, String token) {
        //学生列表
        List<Student> students = new ArrayList<>();
        //分页查询 pageNo 页码，size 页面的条数
        Page<User> page = new Page<>(pageNo, size);
        //分页的查询条件
        QueryWrapper<User> wrapper = null;
        //查询
        Page<User> userPage = null;
        //判断用户权限
        UserDto userDto = getUserInfoByToken(token);
        List<String> roles = userDto.getRoles();
        //判断权限，设置相应的操作
        //老师权限
        if (roles.contains("ROLE_TEACHER") && !(roles.contains("ROLE_ADMIN"))) {
            //获取当前老师下的所有学生编号
            List<Integer> studentNos = relationMapper.selectList(new QueryWrapper<Relation>()
                    .eq("teacherNo", userDto.getUserId()))
                    .stream()
                    .map(Relation::getStudentNo)
                    .collect(Collectors.toList());
            //考虑当前老师没有学生的场景
            if (CollectionUtils.isEmpty(studentNos)) {
                return examUtils.setTableDto();
            }
            //根据编号查询到所有的学生
            wrapper = new QueryWrapper<User>().in("id", studentNos);
            //查询
            userPage = userMapper.selectPage(page, wrapper);
            List<User> studentList = userPage.getRecords();
            //判断是否为空，以及将密码置空
            if (CollectionUtils.isEmpty(userPage.getRecords())) {
                return examUtils.setTableDto();
            }
            //将密码置空
            userPage.getRecords().forEach(u -> u.setPassword(""));
            for (User user : studentList) {
                students.add(new Student(user.getId(), user.getUsername(),
                        examUtils.zeroFillFour(Integer.parseInt(userDto.getUserId())), userDto.getUserName()));
            }
            return examUtils.setTableDto(userPage.getTotal(), students);
        } else {
            //管理员权限
            wrapper = new QueryWrapper<User>().eq("descn", STUDENT);
            //查询
            userPage = userMapper.selectPage(page, wrapper);
            //获取分页的数据
            List<User> userList = userPage.getRecords();
            //判断是否为空，以及将密码置空
            if (!CollectionUtils.isNotEmpty(userPage.getRecords())) {
                return examUtils.setTableDto();
            }
            //将密码置空
            userPage.getRecords().forEach(u -> u.setPassword(""));
            for (User user : userList) {
                synchronized (this) {
                    //根据学生编号到关系表中查询其老师的信息
                    User teacherUser = userMapper.selectById(relationMapper.selectOne(new QueryWrapper<Relation>()
                            .eq("studentNo", user.getId()))
                            .getTeacherNo());
                    //将学生信息和老师信息添加到学生老师关系列表里
                    students.add(new Student(user.getId(), user.getUsername(),
                            examUtils.zeroFillFour(user.getId()), teacherUser.getUsername()));
                }
            }
        }

        return examUtils.setTableDto(userPage.getTotal(), students);
    }

    @Override
    public void insertStudentUser(StudentVo studentVo) {
        User studentUser = new User();
        //判断账户中是否有这个名称
        User user = userMapper.selectOne(new QueryWrapper<User>().eq("username", studentVo.getStudentName()));
        if (null != user) {
            throw new ExamException("当前学生账户已存在...");
        }
        studentUser.setUsername(studentVo.getStudentName());
        studentUser.setPassword(DEFAULT_PASSWORD);
        studentUser.setStatus(DEFAULT_STATUS);
        studentUser.setDescn(STUDENT);
        log.info("当前新增的学生信息是:" + studentUser);
        //新增学生
        userMapper.insert(studentUser);
        //获取当前新增学生的主键值
        Integer studentNo = studentUser.getId();
        log.info("当前新增的学生编号是:" + studentNo);
        //向角色表插入信息
        UserRole studentUserRole = new UserRole();
        studentUserRole.setUserId(studentNo);
        studentUserRole.setRoleId(STUDENT_PERMIT_VALUE);
        userRoleMapper.insert(studentUserRole);
        //向师生关系表中插入数据
        Relation studentRelation = new Relation();
        studentRelation.setStudentNo(studentNo);
        studentRelation.setTeacherNo(studentVo.getTeacherNo());
        relationMapper.insert(studentRelation);
    }

    @Override
    public void updateStudentUserInfo(StudentVo studentVo, String token) {
        //获取权限
        UserDto userDto = getUserInfoByToken(token);
        List<String> roles = userDto.getRoles();
        //打印当前修改的信息，和对应的权限
        log.info("修改的学生所有信息是:" + studentVo + "-权限-" + Arrays.toString(roles.toArray()));
        if (roles.contains("ROLE_ADMIN")) {
            //修改学生用户信息
            userMapper.update(new User(), new UpdateWrapper<User>().eq("id", studentVo.getStudentNo())
                    .set("username", studentVo.getStudentName()));
            //修改学生所属老师信息
            relationMapper.update(new Relation(), new UpdateWrapper<Relation>()
                    .eq("studentNo", studentVo.getStudentNo())
                    .set("teacherNo", studentVo.getTeacherNo()));
        }
        if (!roles.contains("ROLE_ADMIN") && roles.contains("ROLE_TEACHER")) {
            //修改学生用户信息
            userMapper.update(new User(), new UpdateWrapper<User>().eq("id", studentVo.getStudentNo())
                    .set("username", studentVo.getStudentName()));
        }
    }

    @Override
    public Teacher getteacherInfoByStudentNo(Integer studentNo) {
        User user = userMapper.selectOne(new QueryWrapper<User>().eq("id",
                relationMapper.selectOne(new QueryWrapper<Relation>()
                        .eq("studentNo", studentNo)).getTeacherNo()));
        return new Teacher(examUtils.zeroFillFour(user.getId()), user.getUsername());
    }

    @Override
    public void deleteStudentUsers(List<Integer> studentNos) {
        userMapper.deleteBatchIds(studentNos);
        //注意，用户表的主键字段是id，而删除角色表时，删除的字段是 user_id
        userRoleMapper.delete(new QueryWrapper<UserRole>().in("user_id", studentNos));
        //注意，用户表的主键字段是id，而删除师生关系表时，删除的字段是 studentNo
        relationMapper.delete(new QueryWrapper<Relation>().in("studentNo", studentNos));
    }

    @Override
    public void batchImportStudent(String token, String pathName) {
        UserDto userDto = getUserInfoByToken(token);
        if (userDto.getRoles().contains("ROLE_ADMIN")) {
            throw new ExamException();
        }
        File file = new File(UPLOAD_ADDRESS_PREFIX + File.separator + pathName);
        if (!file.exists()) {
            throw new ExcelImportException("请联系管理员添加Excel模板...");
        }
        String teacherNo = userDto.getUserId();
        ImportParams params = new ImportParams();
        params.setTitleRows(1);
        params.setHeadRows(1);
        List<Student> list = ExcelImportUtil.importExcel(
                //正确格式的模板对应的数据导入
                file,
                Student.class, params);
        List<String> studentNameList = list.stream().map(Student::getStudentName).collect(Collectors.toList());
        for (String studentName : studentNameList) {
            synchronized (this) {
                StudentVo studentVo = new StudentVo();
                studentVo.setStudentName(studentName);
                studentVo.setTeacherNo(Integer.parseInt(teacherNo));
                insertStudentUser(studentVo);
            }
        }

        //后面还有很多Excel模板累的属性偷懒不写了...
    }

    @Override
    public void updateUserStatus(Integer userId) {
        userMapper.update(new User(),new UpdateWrapper<User>().eq("id",userId).set("status",1));
    }


}
